#include "TargetWindow.h"

#define PT_PATH		0

TargetWindow::TargetWindow(int w, int h, GBCSolution* Gsln, GBCSolution* GMEM1, GBCSolution* GMEM2, GBCSolution* GMEM3) : Fl_Window(w,h,"Solution Visualization Tools"){
	
	// Store a pointer to the calling solution.
	gsln=Gsln;
	mem1=GMEM1;
	mem2=GMEM2;
	mem3=GMEM3;
	
	x_click=0;
	y_click=0;
	
	begin();
	this->size_range(0,0,1600,1600);
	
	// Put a simple menu, for saving or closing the window.
	menu = new Fl_Menu_Bar(0,0,w,25,"MENU");

	Fl_Menu_Item items[]= {
	    {"&File",0,0,0,FL_SUBMENU},
		 {"Save as..",0,0,0,FL_SUBMENU | FL_MENU_INACTIVE},
		    {"PNG (.png)",0,(Fl_Callback*)cb_nothing,this,0},
			{"JPEG (.jpg)",0,(Fl_Callback*)cb_nothing,this,0},
			{"Bitmap (.bmp)",0,(Fl_Callback*)cb_nothing,this,0},
			{"Adobe PDF (.pdf)",0,(Fl_Callback*)cb_nothing,this,0},
			{0},
		  {"&Close",0,(Fl_Callback*)cb_Close,this,0},
		  {0},			
		{0}
	};
	
	menu->copy(items);
	menu->down_box(FL_NO_BOX);

	// Put in a range slider for selecting the chart's range.	
	sl_x = new Fl_Value_Slider(10,30,270,30,"Range (yds)");
	sl_x->align(FL_ALIGN_RIGHT);
	sl_x->maximum(gsln->MaxRows());
	sl_x->type(FL_HOR_NICE_SLIDER);
	sl_x->callback(cb_slUpdate,this);
	sl_x->precision(0);

	// normal guns
	if (gsln->MaxRows() > 200) {
	   sl_x->step(10);
	   sl_x->value(200);
	   sl_x->minimum(50);
	   }
	else { // slow projectiles like Tranq darts and paintballs?
	   sl_x->step(5);
	   sl_x->value(30);
	   sl_x->minimum(15);
	   }

	btPerspective=new Fl_Check_Button(380,28,20,20,"Perspective");
	btPerspective->value(1);
	btPerspective->callback(cb_Perspective,this);
	btPerspective->tooltip("Apply perspective to bullet path");

	btTargetPos=new Fl_Check_Button(380,46,20,20,"Target at POI");
	btTargetPos->value(1);
	btTargetPos->callback(cb_TargetPos,this);
	btTargetPos->tooltip("Draw the target at the point of impact or point of aim");
	
	Fl_Menu_Item guideitems[]={
	   {"None",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"1,5 MIL",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"1-5 MIL",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"1-10 MIL",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"1-5,10 MIL",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"2,10 MOA",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"2,4,6,8,10 MOA",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"2-20 MOA",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"2-10,20 MOA",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"1-5 Degrees",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"5,10,15 Degree",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"10,20,30,40,50 Digisight N750 clicks",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"10,20..90 Digisight N750 clicks",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"10,20,30,40,50 Digisight N960 clicks",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"10,20..90 Digisight N960 clicks",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"Digisight N960 Reticle 3",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"Digisight N960 Reticle 4",0,(Fl_Callback*)cb_Guides, this, 0},
	   {"Digisight N960 Reticle 5",0,(Fl_Callback*)cb_Guides, this, 0},
	   {0}
	   };
	cScopeGuides = new Fl_Choice(330,65,150,30,"Scope Guides");
	cScopeGuides->copy(guideitems);
	cScopeGuides->value(1);
	
	btMem1 = new Fl_Check_Button(490,30,20,20,"Memory 1");
	btMem2 = new Fl_Check_Button(490,50,20,20,"Memory 2");
	btMem3 = new Fl_Check_Button(490,70,20,20,"Memory 3");
	btMem1->align(FL_ALIGN_RIGHT);
	btMem2->align(FL_ALIGN_RIGHT);
	btMem3->align(FL_ALIGN_RIGHT);
	btMem1->labelcolor(FL_RED);
	btMem2->labelcolor(FL_DARK_GREEN);
	btMem3->labelcolor(FL_BLACK);
	btMem1->callback(cb_Mem1,this);
	btMem2->callback(cb_Mem2,this);
	btMem3->callback(cb_Mem2,this);
	
	if (mem1==NULL) btMem1->deactivate();
	if (mem2==NULL) btMem2->deactivate();
	if (mem3==NULL) btMem3->deactivate();
	
	if (mem1!=NULL) btMem1->tooltip(mem1->Name());
	if (mem2!=NULL) btMem2->tooltip(mem2->Name());
	if (mem3!=NULL) btMem3->tooltip(mem3->Name());
	
#define TARGET_COUNT 19
	Fl_Menu_Item plottypeitems[TARGET_COUNT+1]={
	   {"None",0,(Fl_Callback*)cb_TargetType, this, 0},
	   
	   {"1/2\" Rings",0,(Fl_Callback*)cb_TargetType, this, 0},
	   {"1\" Rings",0,(Fl_Callback*)cb_TargetType, this, 0},
	   {"2\" Rings",0,(Fl_Callback*)cb_TargetType, this, 0},
	   
	   {"ISSF 10m Air Pistol",0,(Fl_Callback*)cb_TargetType, this, 0},
	   {"ISSF 10m Air Rifle",0,(Fl_Callback*)cb_TargetType, this, 0},
	   {"ISSF 25m Rapid Fire Pistol",0,(Fl_Callback*)cb_TargetType, this, 0},
	   {"ISSF 25m Pistol",0,(Fl_Callback*)cb_TargetType, this, 0},
	   {"ISSF 50m Pistol",0,(Fl_Callback*)cb_TargetType, this, 0},

	   {"ISSF 50m Rifle",0,(Fl_Callback*)cb_TargetType, this, 0},
	   {"ISSF 300m Rifle",0,(Fl_Callback*)cb_TargetType, this, 0},
	   
	   {"NRA 200 yard reduced for 100yd",0,(Fl_Callback*)cb_TargetType, this, 0},
	   {"NRA 200 yard",0,(Fl_Callback*)cb_TargetType, this, 0},
	   {"NRA 300 yard",0,(Fl_Callback*)cb_TargetType, this, 0},
	   {"NRA 600 yard",0,(Fl_Callback*)cb_TargetType, this, 0},
	   {"NRA 800 yard+",0,(Fl_Callback*)cb_TargetType, this, 0},
	   
	   {"2' x 6' Rectangle",0,(Fl_Callback*)cb_TargetType, this, 0},
	   {"6\" x 4' Rectangle",0,(Fl_Callback*)cb_TargetType, this, 0},
	   {"24\" x 14\" Rectangle",0,(Fl_Callback*)cb_TargetType, this, 0},
	   {0}
	   };
	cTargetType = new Fl_Choice(60,65,160,30,"Target");
	cTargetType->copy(plottypeitems);
	cTargetType->value(2);
	
	end();
	show();
	
	
}

TargetWindow::~TargetWindow(void){

	delete menu;
	delete sl_x;
	delete btMem1;
	delete btMem2;
	delete btMem3;
	delete cScopeGuides;
	delete cTargetType;
	delete btTargetPos;
	

}


void TargetWindow::draw(void){
	
	// Let the window draw itself first.
	Fl_Window::draw();
	
	PlotPath(PT_PATH);
}

int TargetWindow::handle(int evt){

	if (evt==FL_PUSH && Fl::event_button1()){
		x_click=Fl::event_x();
		y_click=Fl::event_y();
		this->redraw();
			
	}
	

	
	return Fl_Window::handle(evt);

}



void TargetWindow::PlotImpact(int px, int py, double r1, double r2, int label) {

  Fl_Color col=fl_color();
  if (r1>0) {
     //fl_color(FL_YELLOW);
     fl_pie(px - (r1*x_scale) +1, py-(r1*y_scale) +1, r1*2*x_scale, r1*2*y_scale, 0, 360);
     }
   
  if (r2>r1) {
     fl_color(fl_gray_ramp(FL_NUM_GRAY * 2 / 4));
     fl_arc(px - (r2*x_scale) +1, py-(r2*y_scale) +1, r2*2*x_scale, r2*2*y_scale, 0, 360);
     }

  if (label>0) {
     char txt[10];
     sprintf(txt, "%d", label);
     fl_color(FL_BLACK);
     fl_draw(txt, px+10, py+7);
     }

  fl_color(col);
}

/* 
    PlotMem()
    
    Plot the requested data from a given GBCSolution (the current solution or a memory)
    
    mode = 0: Path, 1: Energy, 2: Velocity, 3: Wind Drift

*/

static void apply_perspective(double t_range, int range, double *x, double *y)
{
   if (range==t_range) return;
   (*y)*=t_range / (double)(range+1);
   (*x)*=t_range / (double)(range+1);
}

void TargetWindow::PlotMem(GBCSolution* mem, int mode){

  double x1,x2;
  double y1,y2;
  double calibre=0.5, r;
  int px1, px2, py1,py2;
  int range, step;
  int impact_ticks = 200;

  int m=this->gsln->MaxRows();
  double t_range=sl_x->value();
  int perspective=btPerspective->value()==1;
  int start = perspective ? 30 : 0;

  if (t_range <= 500) impact_ticks=50;
  else if (t_range <= 1200) impact_ticks=100;

  step=1;
  if      (t_range > 2000) step = 20;
  else if (t_range > 1000) step = 10;
  else if (t_range > 500)  step = 5;
  
  if (mem->MaxRows() <= 200) {
     impact_ticks=5;
     start = perspective ? t_range / 5 : 0;
     }
  else if (mem->MaxRows()<=8000) {
     if (impact_ticks>50) impact_ticks=50;
     }
     
// printf("t_range=%1.2f,   impact_ticks=%d,  maxrows=%d\n", t_range, impact_ticks, mem->MaxRows());
     
  x1=mem->GetWindage(start);
  y1=mem->GetPath(start);

  if (perspective) {
     apply_perspective(t_range, start, &x1, &y1);
     }

  for (int n=start+step;n<=t_range && n<m ; n+=step){
	x2=mem->GetWindage(n);
	y2=mem->GetPath(n);

	if (perspective && n<t_range) {
	   apply_perspective(t_range, n, &x2, &y2);
	   }
	   
	// Translate the x,y values into scaled pixels.
	px1=(int)(x0-x_scale*(double)x1);
	px2=(int)(x0-x_scale*(double)x2);
	
	py1=(int)(y0-y_scale*(double)y1);
	py2=(int)(y0-y_scale*(double)y2);

	if (n>50) {
	   if (py2>=ymax) break;
	   if (px2>=xmax) break;
	   }
	
	// Plot the points.
	if (px1>xmin && px1<xmax && py1<ymax && py1>ymin)
		fl_line(px1,py1,px2,py2);

	// mark the line every impact_ticks yards
	if ((n>0) && (n!=t_range) && (n % impact_ticks==0)) {
	   r=(1.0 * n / 100)/2;
	   if (n>100) r+= (n-100)/1000;
	   PlotImpact(px2, py2, calibre/2, r, n);
	   }
	
	x1=x2;
	y1=y2;
	}

   // draw a "bullet hole" at the impact point
   // and a 1-MOA ring (predicted impact variance)
   // plus 0.1 MOA for every 100 yds over 100 yds (rough spin drift allowance)
   r=(1.0 * t_range / 100.0)/2;
   if (t_range > 100) r += (t_range - 100)/1000;
   PlotImpact(px2, py2, calibre/2, r, t_range);
}


/*
   SetupPlot()
   
   Calculate some common parameters for the plots
   This code was common to all plots (Path, Energy, Velocity) and was consolidated here
   
   Requires x_range and y_range to be set before calling
*/

int get_ticks(int range) {
	if (range < 100) return 10;
	if (range < 200) return 20;
	if (range < 400) return 50;
	if (range < 800) return 100;
	if (range < 1600) return 200;
	if (range < 3200) return 500;
	if (range < 6400) return 1000;
	if (range < 12800) return 2000;
	if (range < 25600) return 4000;
	if (range < 51200) return 8000;
	if (range < 102400) return 16000;
	if (range < 204800) return 32000;
	return 64000;
}

void TargetWindow::SetupPlot(void){

	int w = this->w();
	int h = this->h();
	
	xmin = 5;
	xmax = w-5;
	ymin = 100;
	ymax = h-5;

	x_scale = (double)(xmax-xmin) / (double)x_range;
	y_scale = (double)(ymax-ymin) / (double)y_range;
	
	x_ticks=get_ticks(x_range);
	y_ticks=get_ticks(y_range);
}


/*
   DrawPlotBackground()
   
   Draw the graph background, axis, scale etc
   Code was common to all plots and has been consolidated here
   
   Requires SetupPlot to have been called and y0 set first
*/

void TargetWindow::DrawPlotBackground(int mode, const char *ylabel){

	// Draw the x-axis.
	fl_color(FL_BLACK);
	fl_line_style(FL_SOLID,2);
	fl_line(xmin,y0,xmax,y0);
	
	// Draw the y-axis
	fl_line(x0,ymin,x0,ymax);

	// with perspective on, we don't want any confusing inch grids
	if (btPerspective->value()==1) return;

	double px;
	fl_color(FL_BLACK);
	fl_line_style(FL_SOLID,1);
	char txt[64];
	int r=0;
	double txtw=0;

	int tick_top=ymax;
	if (mode != PT_PATH) tick_top=y0;

	// Draw X gridlines every x_ticks yds and label them.
	if (xmin<0) {
	   for (px=x0;px>=xmin;px-=((double)x_ticks*x_scale)){
		//fl_line((int)px,y_bottom,(int)px,y_top);
		fl_line_style(FL_DOT,1);
		fl_line((int)px,(int)ymin,(int)px,tick_top);
		sprintf(txt,"%d",r*x_ticks);
		if (r%2==0 && r>0) {
			txtw=fl_width(txt);
			fl_line_style(FL_SOLID,1);
			fl_line((int)px,y0,(int)px,y0+8);
			fl_draw(txt,(int)((int)px-(txtw/2)),y0+10+fl_height());
		}
		r++;
	   }
	}

	// Draw X gridlines every x_ticks yds and label them.
	for (px=x0;px<=xmax;px+=((double)x_ticks*x_scale)){
		//fl_line((int)px,y_bottom,(int)px,y_top);
		fl_line_style(FL_DOT,1);
		fl_line((int)px,(int)ymin,(int)px,tick_top);
		sprintf(txt,"%d",r*x_ticks);
		if (r%2==0 && r>0) {
			txtw=fl_width(txt);
			fl_line_style(FL_SOLID,1);
			fl_line((int)px,y0,(int)px,y0+8);
			fl_draw(txt,(int)((int)px-(txtw/2)),y0+10+fl_height());
		}
		r++;
	}
	
	// Draw Y hashmarks every 1 y_tick going up.
	double py;
	r=0;
	for (py=y0;py>=ymin;py-=((double)y_ticks*y_scale)){
		if (r>0){
			fl_line(xmin,(int)py,xmax,(int)py);
			sprintf(txt,"+%d%s",r*y_ticks, ylabel);
			fl_draw(txt,xmin+10,(int)(py+fl_height()/2));
		}
		r++;
	}
	
	// Draw Y hashmarks every 1 y_tick going down
	if (mode==PT_PATH) {
	   r=0;
	   for (py=y0;py<=ymax;py+=((double)y_ticks*y_scale)){
		if (r>0){
			fl_line(xmin,(int)py,xmax,(int)py);
			sprintf(txt,"-%d%s",r*y_ticks, ylabel);
			if (py+fl_height()/2<ymax)	fl_draw(txt,xmin+10,(int)(py+fl_height()/2));
		}
		r++;
	   }
	}

}


void TargetWindow::DrawPlotData(int mode){

	fl_color(FL_BLUE);
	fl_line_style(FL_SOLID,1);
	PlotMem(this->gsln, mode);
	
	if (btMem1->value()==1){
		fl_color(FL_RED);
		fl_line_style(FL_SOLID,1);
		PlotMem(mem1, mode);
		}
	if (btMem2->value()==1){
		fl_color(FL_GREEN);
		fl_line_style(FL_SOLID,1);
		PlotMem(mem2, mode);
		}
	if (btMem3->value()==1){
		fl_color(FL_BLACK);
		fl_line_style(FL_SOLID,1);
		PlotMem(mem3, mode);
		}
	
	if (x_click>xmin && x_click<xmax && y_click>ymin && y_click<ymax){
		// If the user clicks, show the coordinates they clicked on.
		double click_label_x= ((x_click)-x0)/x_scale;
		double click_label_y= (y0-y_click)/y_scale;
		char lbl_point[20];
		sprintf(lbl_point,"(%.2f,%.2f)",click_label_x, click_label_y);
		fl_color(FL_RED);
		fl_draw(lbl_point,x_click+5, y_click-5);
		fl_rectf(x_click-2, y_click-2,4,4);
	}

	// Draw the frame around it last to make it all pretty.
	fl_frame("aaaa",5,100,w()-10,h()-105);

}


void TargetWindow::PlotPath(int ptype){

	// We need to find the max and min y-values to determine our y scale and y-ticks
	double _miny=0;
	double _maxy=0;
	double _minx=0;
	double _maxx=0;
	double minsize=20;
	double tx0;
	double ty0;
	
	switch (cTargetType->value()) {
	    case 0: minsize=5;
	    case 1: minsize=6;
	            break;
	    case 2: minsize=12;
	            break;
	    case 3: minsize=12;
	    	    break;
	    case 4: minsize=5;
		    break;
	    case 5: minsize=1.5;
	    	    break;
	    case 6:
	    case 7:
	    case 8: minsize=10;
	            break;
	    case 9: minsize=5;
	    	    break;
	    case 16:
	    case 17:minsize=72;
		    break;
	    }


	t_range = (int)sl_x->value();
	
	for (int e=10;e<=t_range;e++){
	    double y=gsln->GetPath(e);
	    double x=0-gsln->GetWindage(e);
	    if (btPerspective->value()==1) {
	       apply_perspective(sl_x->value(), e, &x, &y);
	       }
	    if (y > _maxy) _maxy=y;
	    if (y < _miny) _miny=y;
	    if (x > _maxx) _maxx=x;
	    if (x < _minx) _minx=x;
	}

	if (_minx > -minsize) _minx=-minsize;
	else _minx *= 1.05;
	if (_maxx <  minsize) _maxx=minsize;
	else _maxx *= 1.05;
	x_range= _maxx - _minx;
	
	_miny*=1.1;
	if (_miny > -minsize) _miny=-minsize;
	else _miny*=1.1;
	if (_maxy < minsize) _maxy=minsize;
	else _maxy*=1.1;
	
	if (_maxy < fabs(_miny)/3) _maxy = fabs(_miny)/3;
	else if (fabs(_miny) < (_maxy/3)) _miny = -_maxy/3;
	
	y_range = _maxy - _miny;

	// keep a fixed aspect ratio
	if (y_range < x_range) y_range=x_range;
	if (x_range < y_range) x_range=y_range;

	SetupPlot();
	
	// keep the plot roughly centred/contained in the window
	y0 = ymin + ((ymax - ymin)/2) + (((_maxy + _miny)/2.0) * y_scale);
	x0 = xmin + ((xmax - xmin)/2) - (((_maxx + _minx)/2.0) * x_scale);
	
	// tweak a bit to see more of the target
	if (btTargetPos->value()==1) {
	   if (gsln->GetPath(sl_x->value())<=0) {
	      y0 -= (minsize/4) * y_scale;
	      }
	   else {
	      y0 += (minsize/4) * y_scale;
	      }
	   }

#if 0
	printf("xmin=%d, xmax=%d, _minx=%lf, _maxx=%lf  x0=%d, wnd=%lf, xscale=%lf\n",
	       xmin, xmax, _minx, _maxx, x0, gsln->GetWindage(sl_x->value()), x_scale);
	printf("ymin=%d, ymax=%d, _miny=%lf, _maxy=%lf  y0=%d, path=%lf, yscale=%lf\n",
	       ymin, ymax, _miny, _maxy, y0, gsln->GetPath(sl_x->value()), y_scale);
	printf("maxy+miny/2 = %lf,  maxx+minx/2=%lf\n", (_maxy + _miny)/2.0, (_maxx + _minx)/2.0);
#endif
	
	tx0 = x0;
	ty0 = y0;
	if (btTargetPos->value()==1) {
	   // set target centre to POI
	   tx0 -= gsln->GetWindage(sl_x->value()) * x_scale;
	   ty0 -= gsln->GetPath(sl_x->value()) * y_scale;
	   }

	// Now do our custom drawing.
	fl_draw_box(FL_FLAT_BOX,xmin,ymin,w()-10,h()-ymin-5,FL_WHITE);

	fl_clip(5,100,w()-10,h()-100);
	fl_line_style(FL_SOLID,1);

	// special target type - not circular  (2' x 6' rectangle)
	if (cTargetType->value() == 16) {
	   fl_color(FL_BLACK);
	   fl_draw_box( FL_FLAT_BOX,
	   		tx0 - (1*12*x_scale), ty0 - (1.5*12*y_scale),
	   		2*12*x_scale, 6*12*y_scale, FL_GRAY);
	   }
	else if (cTargetType->value() == 17) { // (6" x 4' rectangle)
	   fl_color(FL_BLACK);
	   fl_draw_box( FL_FLAT_BOX,
	   		tx0 - (3*x_scale), ty0 - (1*12*y_scale),
	   		6*x_scale, 4*12*y_scale, FL_GRAY);
	   }
	else if (cTargetType->value() == 18) { // (24" x 14" rectangle)
	   fl_color(FL_BLACK);
	   fl_draw_box( FL_FLAT_BOX,
	   		tx0 - (4*x_scale), ty0 - (3*y_scale),
	   		24*x_scale, 14*y_scale, FL_GRAY);
	   }
	    

	// Draw the score circles at 1 inch intervals
	double r, aimring, *rings;
	int i, score, aimring_drawn=0; 
	char txt[10];
	#define mm(x) (x/25.4)
	double aimrings[TARGET_COUNT]=
	        { 0,							// None
	          3, 6, 12,						// 1/2, 1, 2"
	          mm(59.5), mm(30.5), mm(500), mm(200), mm(200), 	// ISSF Pistol
	          mm(112.4), mm(600), 					// ISSF Rifle
	          6.35, 13, 19, 36, 44,					// NRA
	          4,4,4,						// Rectangular
	        };
	double rings_data[TARGET_COUNT][13] = {
		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },			// None
		
		{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0 },		// 1/2" rings
		{ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 0 },		// 1" rings
		{ 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 0 },	// 2" rings

		// ISSF 10m Air Pistol - 11.5mm 10 ring, increasing by 16mm
		{ mm(6.5), mm(11.5), mm(27.5), mm(43.5), mm(59.5), mm(75.5), mm(91.5), mm(107.5), mm(123.5), mm(139.5), mm(155), 0 },
		// ISSF 10m Air Rifle
		{ -0.01, mm(0.5), mm(5.5), mm(10.5), mm(15.5), mm(20.5), mm(25.5), mm(30.5), mm(35.5), mm(40.5), mm(45.5), 0 },
		// ISSF 25m Rapid Pistol - 100mm 10 ring, increasing by 100mm
		{ mm(50), mm(100), mm(180), mm(260), mm(340), mm(420), mm(500), 0 },
		// ISSF 25m Precision Pistol - 50mm 10 ring, increasing by 50mm
		{ mm(25), mm(50), mm(100), mm(150), mm(200), mm(250), mm(300), mm(350), mm(400), mm(450), mm(500), 0 },
		// ISSF 50m Precision Pistol - 50mm 10 ring, increasing by 50mm
		{ mm(25), mm(50), mm(100), mm(150), mm(200), mm(250), mm(300), mm(350), mm(400), mm(450), mm(500), 0 },

		// ISSF 50m Rifle
		{ mm(5.2), mm(10.4), mm(26.4), mm(42.4), mm(58.4), mm(74.4), mm(90.4), mm(106.4), mm(122.4), mm(138.4), mm(154.4), 0 },
		// ISSF 300m Rifle
		{ mm(50), mm(100), mm(200), mm(300), mm(400), mm(500), mm(600), mm(700), mm(800), mm(900), mm(1000), 0 },
		
		{ 1.35, 3.35, 6.35, 9.35, 12.35, 15.35, 18.35, 0 },	// NRA 100 yard reduction of 200 yard
		{ 3, 7, 13, 19, 25, 31, 37, 0 },			// NRA 200 yard
		{ 3, 7, 13, 19, 25, 31, 37, 0 },			// NRA 300 yard
		{ 6, 12, 18, 24, 36, 48, 60, 0 },			// NRA 600 yard
		{ 10, 20, 30, 44, 60, 72, 0 },				// NRA 800 yard+
		
		{ 4, 10, 0 },						// 2'x6' rectangular
		{ 4, 10, 0 },						// 6"x4' rectangular
		{ 4, 10, 0 },						// 12"x8" rectangular
		};
		
	aimring = aimrings[cTargetType->value()];
	rings   = rings_data[cTargetType->value()];
	    
	score=11;
	i=0;
	while (i<=10 && rings[i]!=0) { 
	   i++;
	   score--;
	   }
	   
	// Draw the target
	while (i > 0) {
	    i--;
	    score++;
	    
	    if (rings[i] < 0) continue;
	    r=rings[i]/2;
	    
	    if (rings[i] <= aimring && !aimring_drawn) {
	       fl_color(FL_GRAY);
	       double ar=aimring/2;
	       fl_pie(tx0 - (ar*x_scale), ty0-(ar*y_scale), ar*2*x_scale, ar*2*y_scale, 0, 360);
	       aimring_drawn=1;
	       }
	       
	    if (i==0 && 1) {
	       fl_color(FL_RED);
	       fl_pie(tx0 - (r*x_scale), ty0-(r*y_scale), r*2*x_scale, r*2*y_scale, 0, 360);
	       }
	    else {
	       //fl_color(rings[i] > aimring ? FL_BLACK : FL_WHITE);
	       fl_color(FL_BLACK);
	       fl_arc(tx0 - (r*x_scale), ty0-(r*y_scale), r*2*x_scale, r*2*y_scale, 0, 360);
	       }	       
	    
	    //fl_color(rings[i] > aimring ? FL_BLACK : FL_WHITE);
	    fl_color(FL_BLACK);
	    
	    // draw the score centred in the ring
	    if (score>9 && r < 0.9) {
	       // don't bother to draw score as ring is too small
	       }
	    else if (score==11 || i==0) {
	       fl_draw("X", tx0 + 1, ty0 - 5);
	       }
	    else {
	       sprintf(txt, "%d", score);
	       double w = (r - (rings[i-1]/2)) / 2;
	       fl_draw(txt, tx0 + ((r - w)*x_scale) - (fl_width(txt)/2) - 1, ty0 - 5);
	       }
	    }
	    
	fl_pop_clip();

	DrawPlotBackground(ptype, "\"");

	// draw scope guides
	if (cScopeGuides->value() > 0) {
	   int sgmode=cScopeGuides->value();
	   double pix_inset=50;
	   int longtick_mod=0;
	   int longtick_val=0;
	   int posts=0, postsize=1;
	   int labeloffset=0,labeloffset2=0;

	   // one MIL - not quite equal to one real milliradian
	   long double oneunit = 3.1415 / 3200;

// convert mm@100m to one step unit (digisight pixel) on plot
#define digisight_unit(mm) (((mm*0.9144)/25.4)*(oneunit / 3.438))

	   if (sgmode >= 15)
	        oneunit=digisight_unit(17) * 6; // Digisight N960 Reticle 3/4/5
	   else if (sgmode >= 13)
	   	oneunit=digisight_unit(17); // use 17mm@100m (One Digisight N960 click)
	   else if (sgmode >= 11)
	   	oneunit=digisight_unit(13); // use 13mm@100m (One Digisight N750 click)
	   else if (sgmode >= 9)
	        oneunit=0.0174532925; // use one degree
	   else if (sgmode >= 5)
	        oneunit=oneunit / 3.438; // use one moa instead
	   
	   double range_inset=pix_inset/x_scale;
	   double radius, dotradius;
	   int start, stop, step, step2, g, dotwidth, dotheight;
	   char txt[10];
	   
	   step2=0;
	   switch (sgmode) {
	      case 1: start=1; stop=5;  step=4; break;		 // 1,5 MIL
	      case 2: start=1; stop=5;  step=1; break;		 // 1-5 MIL
	      case 3: start=1; stop=10; step=1; break;		 // 1-10 MIL
	      case 4: start=1; stop=5;  step=1; step2=5; break;	 // 1-5,10 MIL
	      case 5: start=2; stop=10; step=8; break;		 // 2,10 MOA
	      case 6: start=2; stop=10; step=2; break;		 // 2,4,6,8,10 MOA
	      case 7: start=2; stop=20; step=2; break;		 // 2-20 MOA
	      case 8: start=2; stop=10; step=2; step2=10; break; // 2,4,6,8,10,20 MOA
	      case 9:  start=1; stop=5; step=1; break;		 // 1-5 Degrees
	      case 10: start=5; stop=15; step=5; break;		 // 5,10,15 Degrees
	      case 11: start=10; stop=50; step=10; break;	 // 10,20,30,40,50 Digisight clicks
	      case 12: start=10; stop=90; step=10; break;	 // 10,20..90 Digisight clicks
	      case 13: start=10; stop=50; step=10; break;	 // 10,20,30,40,50 Digisight clicks
	      case 14: start=10; stop=90; step=10; break;	 // 10,20..90 Digisight clicks
	      case 15: start=2;  stop=12;  step=1; break;        // Digisight N960 Reticle 3
	      case 16: start=2;  stop=12;  step=1; break;        // Digisight N960 Reticle 4
	      case 17: start=1;  stop=9;   step=1; break;        // Digisight N960 Reticle 5
	      default: start=1;  stop=1;   step=1; break;
	      }

	   if (sgmode>=15 && sgmode<=17) { // Digisight reticles... centre area
              radius = tanf(oneunit*1) * t_range * 36.0;
              if (sgmode==15 || sgmode==16) { // blank the area in the centre
                 fl_color(FL_WHITE);
                 //fl_rectf(x0-(radius*x_scale)-1, y0-(radius*y_scale)-1, (radius*x_scale*2)+3, (radius*y_scale*2)+3);
                 fl_line(x0-(radius*x_scale)-1, y0, x0+(radius*x_scale)+1, y0);
                 fl_line(x0, y0-(radius*y_scale)-1, x0, y0+(radius*y_scale)+1);
                 longtick_mod = 2;
                 longtick_val = 1; // 1=odd ticks are long, 0=even
                 posts=13;
                 postsize=5;
	      } else {
	         posts=10;
	         postsize=5;
	      }
	   
	      fl_color(FL_RED);
	      if (sgmode!=16) { // set illuminated crosshair size
	         radius = tanf(oneunit/6) * t_range * 36.0;
	      }
	      // draw crosshair
	      fl_line(x0-(radius*x_scale), y0, x0+(radius*x_scale), y0);
	      fl_line(x0, y0-(radius*y_scale), x0, y0+(radius*y_scale));
	   }
	   
	   fl_clip(5,100,w()-10,h()-105);
	   fl_line_style(FL_SOLID,2);
	   fl_color(FL_BLACK);
	   
	   dotradius= 0.25/2;
	   dotwidth = tanf(oneunit * dotradius) * t_range * 36.0 * x_scale;
	   dotheight= tanf(oneunit * dotradius) * t_range * 36.0 * y_scale;
	   
	   labeloffset = dotwidth+2;
	   if (longtick_mod>1) labeloffset+=dotwidth;
	   if (gsln->GetWindage(t_range)<0)
	      labeloffset = -labeloffset;
	   
	   for (g=start; g<=stop; g+=step) {
	       radius = tanf(oneunit*g) * t_range * 36.0;
	       int tickscale=1;
	       int lofs=labeloffset;
	       if (longtick_mod>1) {
	          if (longtick_val == (g % longtick_mod)) tickscale=2;
	       }
	       
	       //if (radius <= -_miny || radius <= x_range/2) {
	          //fl_color(radius > aimring ? FL_BLACK : FL_WHITE);
	          fl_line(x0-(radius*x_scale), y0+dotheight*tickscale, x0-(radius*x_scale), y0-dotheight*tickscale);
	          fl_line(x0+(radius*x_scale), y0+dotheight*tickscale, x0+(radius*x_scale), y0-dotheight*tickscale);
	          fl_line(x0-dotwidth*tickscale, y0-(radius*y_scale), x0+dotwidth*tickscale, y0-(radius*y_scale));
	          fl_line(x0-dotwidth*tickscale, y0+(radius*y_scale), x0+dotwidth*tickscale, y0+(radius*y_scale));

	          if (g==start) {
	             if (sgmode>=13) sprintf(txt, "-%d N960", g);
	             else if (sgmode>=11) sprintf(txt, "-%d N750", g);
	             else if (sgmode>=9) sprintf(txt, "-%d Deg", g);
	             else if (sgmode>=5) sprintf(txt, "-%d MOA", g);
	             else sprintf(txt, "-%d MIL", g);
	             }
	          else sprintf(txt, "-%d", g);
	          labeloffset2= (labeloffset>=0) ? labeloffset : labeloffset-fl_width(txt);
	          fl_draw(txt, x0+labeloffset2, y0+(radius*y_scale) + 3);
	          sprintf(txt, "+%d", g);
	          labeloffset2= (labeloffset>=0) ? labeloffset : labeloffset-fl_width(txt);
	          fl_draw(txt, x0+labeloffset2, y0-(radius*y_scale) + 3);
	       //   }
	       }
           if (posts > 0) {
              radius = tanf(oneunit*posts) * t_range * 36.0;
              double radius2 = tanf(oneunit*postsize) * t_range * 36.0;
              fl_rectf(x0-dotwidth*2, y0-((radius+radius2)*y_scale), dotwidth*4, radius2*y_scale);
              fl_rectf(x0-dotwidth*2, y0+(radius*y_scale), dotwidth*4, radius2*y_scale);
              fl_rectf(x0-((radius+radius2)*x_scale), y0-dotheight*2, radius2*x_scale, dotheight*4);
              fl_rectf(x0+(radius*x_scale), y0-dotheight*2, radius2*x_scale, dotheight*4);
              }
	   if (step2 > 0) {
	       g+=step2-step;
	       radius =tanf(oneunit*g) * t_range * 36.0;

	       //if (radius <= -_miny || radius <= x_range/2) {
	          //fl_color(radius > aimring ? FL_BLACK : FL_WHITE);
	          fl_line(x0-(radius*x_scale), y0+dotheight, x0-(radius*x_scale), y0-dotheight);
	          fl_line(x0+(radius*x_scale), y0+dotheight, x0+(radius*x_scale), y0-dotheight);
	          fl_line(x0-dotheight, y0-(radius*y_scale), x0+dotheight, y0-(radius*y_scale));
	          fl_line(x0-dotheight, y0+(radius*y_scale), x0+dotheight, y0+(radius*y_scale));
	          sprintf(txt, "+%d", g);
	          labeloffset2= (labeloffset>=0) ? labeloffset : labeloffset-fl_width(txt);
	          fl_draw(txt, x0+labeloffset2, y0+(radius*y_scale)+3);
	       //   }
	       }

	   // if perspective is on, draw a MIL or MOA or whatever grid
	   if (btPerspective->value()==1) {
	      fl_line_style(FL_DOT,1);
	      start=1;
	      
	      for (g=start; g<=stop; g++) {
	          radius = tanf(oneunit*g) * t_range * 36.0;
	          
	          if ((sgmode>=10) && (g%2)) continue;
	       
	          //if (radius <= -_miny || radius <= x_range/2) {
	             fl_line(xmin, y0-(radius*y_scale), xmax, y0-(radius*y_scale));
	             fl_line(xmin, y0+(radius*y_scale), xmax, y0+(radius*y_scale));
	             fl_line(x0-(radius*x_scale), ymin, x0-(radius*x_scale), ymax);
	             fl_line(x0+(radius*x_scale), ymin, x0+(radius*x_scale), ymax);
	          //   }
	          }
	      }

	   fl_pop_clip();
	   }
	   
	DrawPlotData(ptype);

	return;
}


void TargetWindow::cb_btPlot(Fl_Widget* o, void* v){
	TargetWindow* T = (TargetWindow*)v;
	
	T->damage(255);
	T->draw();


}

void TargetWindow::cb_nothing(Fl_Widget* o, void* v){

	;
}

void TargetWindow::cb_Close(Fl_Widget* o, void* v){
	TargetWindow* T = (TargetWindow*)v;
	T->~TargetWindow();
}

void TargetWindow::cb_slUpdate(Fl_Widget* o, void* v){

	TargetWindow* T = (TargetWindow*)v;
	T->damage(255);
	T->draw();
}

void TargetWindow::cb_Mem1(Fl_Widget* o, void* v){

	TargetWindow* T = (TargetWindow*)v;
	T->damage(255);
	T->draw();

}


void TargetWindow::cb_Mem2(Fl_Widget* o, void* v){

	TargetWindow* T = (TargetWindow*)v;
	T->redraw();

}

void TargetWindow::cb_Guides(Fl_Widget* o, void* v){

	TargetWindow* T = (TargetWindow*)v;
	T->redraw();

}

void TargetWindow::cb_TargetType(Fl_Widget* o, void* v){

	TargetWindow* T = (TargetWindow*)v;
	T->redraw();

}

void TargetWindow::cb_Perspective(Fl_Widget* o, void* v){

	TargetWindow* T = (TargetWindow*)v;
	T->redraw();

}

void TargetWindow::cb_TargetPos(Fl_Widget* o, void* v){

	TargetWindow* T = (TargetWindow*)v;
	T->redraw();

}


